/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package mp.weixin.WXpublic;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Date;

import org.apache.commons.lang.StringEscapeUtils;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ObjectNode;
import org.jdom.JDOMException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import utils.WeiXinUtil;

/**
 *
 * @author 王仲秋
 */
public class WeiXinPublic implements Serializable {

    /**
     * 微信接入时使用的 接入后
     */
    private final String TOKEN;
    /**
     * 消息处理对象 整个消息进入 到消息对象的生成
     */
    private final MessageConvert messageConvert = new MessageConvert();
    private final AccessTokenCache atc;
    /**
     * 消息加密的密钥 不使用加密信息的时候 不需要初始化
     */
    private final String ENCODING_ACES_KEY;
    private final String APPID;
    private final String APPSECRET;

    private WeiXinPublic() {
        TOKEN = null;
        ENCODING_ACES_KEY = null;
        APPID = null;
        APPSECRET = null;
        atc = null;
    }

    /**
     *
     * @param token 微信接入时使用的 接入后不再用
     * @param encodingAcesKey 消息加密的密钥 不使用加密信息的时候 不需要初始化
     * @param appId 公众号开发使用
     * @param appSeceret 公众号开发使用
     * @param ml 消息监听接口 配置不同的消息返回
     * @param atc 实现AccessToken 的缓存 可以根据不同情况使用不同的缓存
     */
    public WeiXinPublic(String token, String encodingAcesKey, String appId, String appSeceret, final MessageListener ml, final AccessTokenCache atc) {
        messageConvert.bindingListener(ml);
        this.TOKEN = token;
        this.ENCODING_ACES_KEY = encodingAcesKey;
        this.APPID = appId;
        this.APPSECRET = appSeceret;
        this.atc = atc;
    }

    /**
     * 每次调用这个接口就会更新一次accessToken
     *
     * @return
     */
    public boolean refreshAcccessToken() {
        int timeDelay = 200;
        //这样设置比微信的时间段 微信默认7200 秒
        AccessToken accessToken = new AccessToken();
        try {
            JsonNode jsono = getAccessTokenJson();
            accessToken.accessToken = jsono.get("access_token").getTextValue();
            accessToken.expiresIn = jsono.get("expires_in").getIntValue();
            accessToken.expiresTime = System.currentTimeMillis() + (accessToken.expiresIn - timeDelay) * 1000;
            atc.cache(accessToken);
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
            return false;
        }
        return true;
    }

    public boolean messageReceive(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException, IOException, Exception {
        InputStream is = request.getInputStream();
        InputStreamReader isr = new InputStreamReader(is, "UTF-8");
        OutputStream os = response.getOutputStream();
        boolean fromWeiXIn = false;
        StringBuilder sb = new StringBuilder();
        BufferedReader br = new BufferedReader(isr);
        String s = "";
        while ((s = br.readLine()) != null) {
            sb.append(s);
        }
        br.close();
        isr.close();
        is.close();
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String echostr = request.getParameter("echostr");
        if (WeiXinUtil.checkSignature(signature, timestamp, nonce, TOKEN)) {
            fromWeiXIn = true;
            if (echostr == null) {
                //这个是交互过程
                try {
                    BaseSendMessage bsm = this.messageConvert.preConvert(sb.toString(), echostr);
                    if (bsm != null) {
                        os.write(bsm.toWeiXinMessageXMLString().getBytes("UTF-8"));
                    } else {
                        os.write("".getBytes());
                    }
                } catch (JDOMException ex) {
                    throw new Exception("微信传入数据格式异常，不是一个有效的xml。");
                }
            } else {
                //这个是首次接入的时候 的验证 需要将echostr 原样返回
                os.write(echostr.getBytes());
            }
        }
        os.flush();
        os.close();
        return fromWeiXIn;
    }

    /**
     * 取到就正常运行 取不到就抛异常
     *
     * @return
     * @throws Exception
     */
    private JsonNode getAccessTokenJson() throws Exception {
        StringBuilder url = new StringBuilder();
        url.append("https://api.weixin.qq.com/cgi-bin/token?");
        url.append("grant_type=client_credential");
        url.append("&appid=").append(APPID);
        url.append("&secret=").append(APPSECRET);
        String jsonStr = WeiXinUtil.getHttpBackDomStr(url.toString());
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode jsono = objectMapper.readTree(jsonStr);
        String accessToken = jsono.get("access_token").getTextValue();
        if (accessToken == null) {
            throw new Exception(jsonStr);
        }
        return jsono;
    }

    public String getAccessTokenRealTime() throws Exception {
        JsonNode jsono = getAccessTokenJson();
        return jsono.get("access_token").getTextValue();
    }

    public String getAccessTokenFromCache() {
        if (this.atc == null || this.atc.getCache() == null || this.atc.getCache().expiresTime <= System.currentTimeMillis()) {
            return null;
        } else {
            return this.atc.getCache().accessToken;
        }
    }

    public WeiXinUserEntity getUser(String openId) {
        ObjectMapper objectMapper = new ObjectMapper();
        String infoUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN";
        WeiXinUserEntity weiXinUserEntity = new WeiXinUserEntity();
        try {
            JsonNode ifjson = objectMapper.readTree(WeiXinUtil.getHttpBackDomStr(String.format(infoUrl, getAccessTokenFromCache(), openId)));
            weiXinUserEntity.setSubscribe(ifjson.get("subscribe").getIntValue());
            weiXinUserEntity.setOpenid(ifjson.get("openid").getTextValue());
            weiXinUserEntity.setNickname(ifjson.get("nickname").getTextValue());
            weiXinUserEntity.setSex(ifjson.get("sex").getIntValue());
            weiXinUserEntity.setLanguage(ifjson.get("language").getTextValue());
            weiXinUserEntity.setCity(ifjson.get("city").getTextValue());
            weiXinUserEntity.setProvince(ifjson.get("province").getTextValue());
            weiXinUserEntity.setCountry(ifjson.get("country").getTextValue());
            weiXinUserEntity.setHeadimgurl(ifjson.get("headimgurl").getTextValue());
            weiXinUserEntity.setSubscribeTime(new Date(ifjson.get("subscribe_time").getLongValue()));
            if (ifjson.has("unionid")) {
                weiXinUserEntity.setUnionid(ifjson.get("unionid").getTextValue());
            }
            weiXinUserEntity.setRemark(ifjson.get("remark").getTextValue());
            weiXinUserEntity.setGroupid(ifjson.get("groupid").getIntValue());
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return weiXinUserEntity;
    }

    public String uploadImg(InputStream inputStream, String fileName) {
        StringBuilder sb = new StringBuilder();
        String[] fileNameSp = fileName.split("\\.");
        if (fileNameSp.length < 2) {
            return null;
        }
        String urlStr = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=" + this.getAccessTokenFromCache();
        String BOUNDARY = "----------" + System.currentTimeMillis();

        // 请求正文信息  
        // 第一部分：  
        StringBuilder boundary = new StringBuilder();
        boundary.append("--"); // 必须多两道线  
        boundary.append(BOUNDARY);
        boundary.append("\r\n");
        boundary.append("Content-Disposition: form-data;name=\"file\";filename=\"").append(fileName).append("\"\r\n");
        boundary.append("Content-Type:application/octet-stream\r\n\r\n");
        try {
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            //gt 2015-05-07
            conn.setConnectTimeout(10000);// 设置连接主机超时（单位：毫秒）
            conn.setReadTimeout(10000);// 设置从主机读取数据超时（单位：毫秒）
            conn.setDoOutput(true);
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
            conn.setUseCaches(false); // post方式不能使用缓存  
            // 设置请求头信息  
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("Charset", "UTF-8");
            conn.connect();
            OutputStream os = conn.getOutputStream();

            byte[] b = new byte[1024 * 1024];
            os.write(boundary.toString().getBytes("utf-8"));
            while (true) {
                int read = inputStream.read(b);
                //判断是不是读到了数据流的末尾 ，防止出现死循环。  
                if (read == -1) {
                    break;
                }
                os.write(b);
            }
            os.write(("\r\n--" + BOUNDARY + "--\r\n").getBytes("utf-8"));
            inputStream.close();
            os.close();
            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String str;
            while ((str = br.readLine()) != null) {
                sb.append(str);
            }
            br.close();
            conn.disconnect();
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode ifjson = objectMapper.readTree(sb.toString());
            if (ifjson.get("url") != null) {
                String imgUrl = ifjson.get("url").getTextValue();
                return imgUrl;
            }
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public boolean updateMenu(String enumJsonStr) {
        StringBuilder sb = new StringBuilder();
        try {
            String urlStr = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" + this.getAccessTokenFromCache();
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            //gt 2015-05-07
            conn.setConnectTimeout(10000);// 设置连接主机超时（单位：毫秒）
            conn.setReadTimeout(10000);// 设置从主机读取数据超时（单位：毫秒）
            conn.setDoOutput(true);
            OutputStream os = conn.getOutputStream();
            conn.connect();
            os.write(enumJsonStr.getBytes("utf-8"));
            os.close();
            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String str;
            while ((str = br.readLine()) != null) {
                sb.append(str);
            }
            br.close();
            conn.disconnect();
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode ifjson = objectMapper.readTree(sb.toString());
            if (ifjson.get("errcode").getIntValue() == 0) {
                return true;
            }
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return false;
    }

    public static class AccessToken implements Serializable {

        /**
         * 凭证
         */
        private String accessToken;
        /**
         * 凭证时效
         */
        private int expiresIn;
        /**
         * 1970年开始的毫秒数 过期时间
         */
        private long expiresTime;

    }

    /**
     * 通过上传的文件获得文件的media_id 供回复内容时使用
     *
     * @param inputStream 文件输入流
     * @param filelength 文件长度
     * @param fileName 文件名
     * @param type 文件的类型 有 video image voice music .. 可进行拓展
     *
     * @return
     * @throws JSONException
     */
    public String uploadPermanentMedia(
            InputStream inputStream, long filelength, String fileName, String type) {
        try {
            // 拼装请求地址
            String uploadMediaUrl = "http://api.weixin.qq.com/cgi-bin/material/add_material?access_token=" + this.getAccessTokenFromCache();
            URL url = new URL(uploadMediaUrl);
            String result = null;
            String suffix = fileName.substring(fileName.lastIndexOf("."), fileName.length());
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            con.setRequestMethod("POST"); // 以Post方式提交表单，默认get方式
            con.setDoInput(true);
            con.setDoOutput(true);
            con.setUseCaches(false); // post方式不能使用缓存
            // 设置请求头信息
            con.setRequestProperty("Connection", "Keep-Alive");
            con.setRequestProperty("Charset", "UTF-8");

            // 设置边界,这里的boundary是http协议里面的分割符
            String BOUNDARY = "----------" + System.currentTimeMillis();
            con.setRequestProperty("Content-Type",
                    "multipart/form-data; boundary=" + BOUNDARY);
            // 请求正文信息
            // 第一部分：
            StringBuilder sb = new StringBuilder();
            //这块是post提交type的值也就是文件对应的类型值
            sb.append("--"); // 必须多两道线 http 协议头
            sb.append(BOUNDARY);
            sb.append("\r\n");
            sb.append("Content-Disposition: form-data;name=\"type\" \r\n\r\n"); //这里是参数名，参数名和值之间要用两次
            sb.append(type + "\r\n"); //参数的值
            sb.append("--"); // 必须多两道线
            sb.append(BOUNDARY);
            sb.append("\r\n");
            //这里是media参数相关的信息
            sb.append("Content-Disposition: form-data;name=\"media\";filename=\""
                    + fileName + "\";filelength=\"" + filelength + "\" \r\n");
            sb.append("Content-Type:application/octet-stream\r\n\r\n");
            byte[] head = sb.toString().getBytes("utf-8");
            // 获得输出流
            OutputStream out = new DataOutputStream(con.getOutputStream());
            // 输出表头
            out.write(head);
            // 文件正文部分
            // 把文件已流文件的方式 推入到url中

            int bytes = 0;
            byte[] bufferOut = new byte[1024];
            while ((bytes = inputStream.read(bufferOut)) != -1) {
                out.write(bufferOut, 0, bytes);
            }
            inputStream.close();
            // 结尾部分，这里结尾表示整体的参数的结尾，结尾要用"--"作为结束，这些都是http协议的规定
            byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("utf-8");// 定义最后数据分隔线
            out.write(foot);
            out.flush();
            out.close();
            StringBuffer buffer = new StringBuffer();
            BufferedReader reader = null;
            // 定义BufferedReader输入流来读取URL的响应
            reader = new BufferedReader(new InputStreamReader(
                    con.getInputStream(), "utf-8"));
            String line = null;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            if (result == null) {
                result = buffer.toString();
            }
            // 使用JSONNode解析返回结果
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode ifjson = objectMapper.readTree(result.toString());
            //直接返回获取到的数据，在页面上进行将返回值赋值
            return ifjson.toString();
//			if (ifjson.has("media_id")) {
//				String media_id = ifjson.get("media_id").getTextValue();
//			} else {
//			}
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

        }
        return "";
    }

//    public enum SourceMaterial {
//
//        IMG(1, "图片", "bmp/png/jpeg/jpg/gif");
//        private final int value;
//        private final String desc;
//        private final String[] extension;
//
//        SourceMaterial(int value, String desc, String extension) {
//            this.value = value;
//            this.desc = desc;
//            this.extension = extension.split("/");
//        }
//
//        public static SourceMaterial getIMG() {
//            return IMG;
//        }
//
//        public int getValue() {
//            return value;
//        }
//
//        public String getDesc() {
//            return desc;
//        }
//
//        public String[] getExtension() {
//            return extension;
//        }
//
//    }
    /**
     * 获取当前公众号菜单的内容，方便编辑查看
     *
     * @return
     */
    public String getMenu() {
        StringBuilder sb = new StringBuilder();
        try {
            String urlStr = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=" + this.getAccessTokenFromCache();
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            //gt 2015-05-07
            conn.setConnectTimeout(10000);// 设置连接主机超时（单位：毫秒）
            conn.setReadTimeout(10000);// 设置从主机读取数据超时（单位：毫秒）
            conn.setDoOutput(true);
            OutputStream os = conn.getOutputStream();
            conn.connect();
            os.close();
            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String str;
            while ((str = br.readLine()) != null) {
                sb.append(str);
            }
            br.close();
            conn.disconnect();
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode ifjson = objectMapper.readTree(sb.toString());
            if (ifjson.has("is_menu_open")) {
                if (ifjson.get("is_menu_open").getIntValue() == 0) {

                    return "";
                } else {
                    JsonNode beg = ifjson.get("selfmenu_info");
                    JsonNode begin = beg.get("button");
                    for (int i = 0; i < begin.size(); i++) {
                        if (begin.get(i).has("sub_button")) {
                            JsonNode subBut = begin.get(i).path("sub_button");
                            String list = subBut.get("list").toString();
                            if (!subBut.toString().equalsIgnoreCase(list)) {

                                ((ObjectNode) begin.get(i)).put("sub_button", "#" + list + "#");
                            }

                        }

                    }
                    String menu = beg.toString();
                    String menuStr = StringEscapeUtils.unescapeJava(menu);
                    int[] arr = this.count(menuStr, '#');
//                 	menuStr = menuStr.substring(0, arr[0]-1) + menuStr.substring(arr[0] + 1, arr[1]) + menuStr.substring(arr[1] + 2,arr[2] - 1) + 
//                 			menuStr.substring(arr[2] + 1,arr[3]) + menuStr.substring(arr[3] + 2, menuStr.length());
                    StringBuilder sb1 = new StringBuilder();
                    sb1.append(menuStr.substring(0, arr[0] - 1));
                    for (int i = 0; i < arr.length - 1; i++) {
                        if (i % 2 == 0) {
                            sb1.append(menuStr.substring(arr[i] + 1, arr[i + 1]));
                        } else {
                            sb1.append(menuStr.substring(arr[i] + 2, arr[i + 1] - 1));
                        }

                    }
                    sb1.append(menuStr.substring(arr[arr.length - 1] + 2, menuStr.length()));
                    return sb1.toString();
                }
            }

        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return "";
    }

    public static int[] count(String str, char key) {
        int[] count = {};
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (c == key) {
                // 扩展数组,
                count = Arrays.copyOf(count, count.length + 1);
                // 添加序号 i
                count[count.length - 1] = i;
            }
        }
        return count;
    }
//    /**
//     * 获取模板列表
//     */

    public String getTempList() {

        String urlStr = "https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token=" + this.getAccessTokenFromCache();
        String resultStr = this.sendGet(urlStr);
        return resultStr;
    }
//    /**
//     * 获取账号模板设置的行业信息
//     */
//    public String getTempSort(){
//    	return "";
//    }
//    /**
//     * 获取模板id
//     */
//    public String getTempId(){
//    	
//    	return "";
//    }

    /**
     * 模板消息推送
     */
    public String postTemplte(String resultStr) {
        StringBuilder sb = new StringBuilder();
        try {
            String urlStr = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + this.getAccessTokenFromCache();
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            //gt 2015-05-07
            conn.setConnectTimeout(10000);// 设置连接主机超时（单位：毫秒）
            conn.setReadTimeout(10000);// 设置从主机读取数据超时（单位：毫秒）
            conn.setDoOutput(true);
            OutputStream os = conn.getOutputStream();
            conn.connect();
            os.write(resultStr.getBytes("utf-8"));
            os.close();
            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String str;
            while ((str = br.readLine()) != null) {
                sb.append(str);
            }
            br.close();
            conn.disconnect();
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode ifjson = objectMapper.readTree(sb.toString());
            System.out.println(ifjson);
            return ifjson.toString();
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return "";
    }

    //get请求不带参数的方法
    public static String sendGet(String requestUrl) {
        StringBuilder sb = new StringBuilder();
        JsonNode ifjson = null;
        try {
            // 建立连接
            URL url = new URL(requestUrl);
            HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();
            httpUrlConn.setDoInput(true);
            httpUrlConn.setRequestMethod("GET");

            // 获取输入流
            InputStream inputStream = httpUrlConn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            // 读取返回结果
            String str = null;
            while ((str = bufferedReader.readLine()) != null) {
                sb.append(str);
            }

            // 释放资源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            httpUrlConn.disconnect();
            ObjectMapper objectMapper = new ObjectMapper();
            ifjson = objectMapper.readTree(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ifjson.toString();
    }
}
